﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using NPMonitor.Interfaces;
using NPMonitor.Attributes;
using NPMonitor.DataModel;

namespace NPMonitor.Implementation
{
    public class JobFileLoader : MarshalByRefObject
    {
        public JobFileInfo RetrieveJobFileInfo(string filePath)
        {
            try
            {
                Assembly assembly = Assembly.LoadFile(filePath);
                Type[] fileType = assembly.GetTypes();

                Type jobType = null;
                foreach (var item in fileType)
                {
                    Type t_type = item.GetInterfaces().FirstOrDefault(t => t.GUID.Equals(typeof(IJob).GUID));

                    if (t_type != null)
                    {
                        jobType = item;
                        break;
                    }
                }

                if (jobType == null)
                {
                    return null;
                }

                IList<Scenario> scenarios = RetrieveScenarioByType(jobType);

                JobFileInfo fileInfo = new JobFileInfo()
                {
                    FileName = Path.GetFileName(filePath),
                    TypeName = jobType.FullName,
                    FileVersion = assembly.GetName().Version,
                    Scenarios = scenarios
                };

                return fileInfo;
            }
            catch
            {
                return null;
            }
        }

        public IList<Scenario> RetrieveScenarioByType(Type jobType)
        {
            IList<Scenario> scenarios = new List<Scenario>();

            MethodInfo[] methods = jobType.GetMethods();

            foreach (var m in methods)
            {
                object attr = m.GetCustomAttributes(typeof(ScenarioAttribute), true).FirstOrDefault();

                if (attr != null)
                {
                    ScenarioAttribute sAttr = attr as ScenarioAttribute;
                    scenarios.Add(new Scenario(sAttr));
                }
            }

            return scenarios;
        }

        #region Static Methods

        public static IJob NewJobInstance(AppDomain appDomain, int jobId)
        {
            IJob jobInstance = null;

            JobDefinition jobDefinition = StoreManager.Current.JobDefinitionStore.GetJobDefinitionById(jobId);

            if (jobDefinition == null)
            {
                throw new Exception("Cannot retrieve jobDefinition of job with id '" + jobId + "'");
            }

            Service.CheckAndUpdateJobDefinition(jobDefinition);

            string execAssemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
                Configuration.ConfigurationSettings.SettingsProvider.JobExecutionFolder,
                Configuration.ConfigurationSettings.SettingsProvider.JobFileFolder,
                jobDefinition.FileName);

            jobInstance = appDomain.CreateInstanceFromAndUnwrap(execAssemblyPath, jobDefinition.TypeName) as IJob;

            return jobInstance;
        }

        public static IJob NewJobInstance(AppDomain appDomain, WorkflowJobData wfJobData)
        {
            IJob jobInstance = NewJobInstance(appDomain, wfJobData.JobId);

            return jobInstance;
        }

        public static JobFileInfo GetJobFileInfo(string jobFilePath)
        {
            AppDomain appDomain = null;
            JobFileLoader fileLoader = null;

            try
            {
                appDomain = JobFileLoader.NewJobUploadDomain();
                fileLoader = JobFileLoader.NewLoaderInstance(appDomain);

                JobFileInfo fileInfo = fileLoader.RetrieveJobFileInfo(jobFilePath);

                return fileInfo;
            }
            finally
            {
                if (appDomain != null)
                {
                    AppDomain.Unload(appDomain);
                }

                if (fileLoader != null)
                {
                    fileLoader = null;
                }
            }
        }

        public static JobFileLoader NewLoaderInstance(AppDomain appDomain)
        {
            Type loaderType = typeof(JobFileLoader);

            string codeBase = Assembly.GetExecutingAssembly().GetName().CodeBase;

            JobFileLoader fileLoader = (JobFileLoader)appDomain.CreateInstanceFromAndUnwrap(codeBase, loaderType.FullName);

            return fileLoader;
        }

        public static AppDomain NewJobUploadDomain()
        {
            AppDomainSetup domainInfo = new AppDomainSetup
            {
                ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                PrivateBinPath = "bin"
            };

            AppDomain appDomain = AppDomain.CreateDomain("JobFileDomain", null, domainInfo);

            return appDomain;
        }

        public static AppDomain NewJobExecutionDomain()
        {
            string execDomainBinPath = System.IO.Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory,
                Configuration.ConfigurationSettings.SettingsProvider.JobExecutionFolder
                );

            AppDomainSetup executorDomainSetup = new AppDomainSetup()
            {
                ApplicationName = "Job Execution",
                ConfigurationFile= AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
                ApplicationBase = execDomainBinPath,
                PrivateBinPath = Configuration.ConfigurationSettings.SettingsProvider.JobLibraryFolder
            };

            AppDomain execDomain = AppDomain.CreateDomain("Job Execution Domain", null, executorDomainSetup);

            return execDomain;
        }

        #endregion
    }
}
